using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
using HuaXianQu.ProtoBuffEx.Runtime;
using HuaXianQu.ProtoBuffEx.Runtime.ProtoInterface;

public class ProtoDataCenter
{
    private static ProtoDataCenter _instance;

    public virtual string ProtoDataPath => $"{ProtoPathConfig.Inst.GenerateProtoDataPath}";
    public virtual string ProtoDataPathIn =>
        $"Assets/Samples/ExcelToProtobuffTool/{ProtoBuffPackageConfig.Version}/Samples/Res/ProtoData";

    private ConcurrentDictionary<Type, IMessage> protoDataDict = new ConcurrentDictionary<Type, IMessage>();

    private static Lazy<ProtoDataCenter> _Lazy = new Lazy<ProtoDataCenter>(() => new ProtoDataCenter());
    private static ProtoDataCenter Inst => _Lazy.Value;

    public static T Get<T>(Action<T> callFun = null) where T : class, IMessage, new()
    {
        return Inst._Get(callFun);
    }

    public static Task<T> GetAsync<T>(Action<T> callFun = null, CancellationToken token = default)
        where T : class, IMessage, new()
    {
        return Inst._GetAsync(callFun, token);
    }

    public static void Reset()
    {
        _Lazy = new Lazy<ProtoDataCenter>(() => new ProtoDataCenter());
    }

    public T _Get<T>(Action<T> callFun) where T : class, IMessage, new()
    {
        Type type = typeof(T);
        T messageData = (T)protoDataDict.GetOrAdd(type, _ =>
        {
            T messageData = Activator.CreateInstance(typeof(T)) as T;
            if (GetBytes(type.Name, out byte[] protoData))
            {
                messageData.MergeFrom(protoData);
                if (messageData is IProtoInit protoInit)
                {
                    protoInit.Init();
                }
            }

            return messageData;
        });

        callFun?.Invoke(messageData);
        return messageData;
    }

    public virtual bool GetBytes(string dataFileName, out byte[] data)
    {
        data = null;
       string dataFileNameTemp = $"{ProtoDataPath}/{dataFileName}.protodat";
        if (!File.Exists(dataFileNameTemp))
        {
            dataFileNameTemp = $"{ProtoDataPathIn}/{dataFileName}.protodat";
        }
        if (!File.Exists(dataFileNameTemp))
        {
            ProtoDebug.LogError($"数据文件不存在：{dataFileNameTemp}");
            return false;
        }
        byte[] protoData = File.ReadAllBytes(dataFileNameTemp);
        if (protoData.Length == 0)
        {
            return false;
        }

        data = protoData;
        return true;
    }

    public async Task<T> _GetAsync<T>(Action<T> callFun, CancellationToken token = default)
        where T : class, IMessage, new()
    {
        Type type = typeof(T);
        if (protoDataDict.TryGetValue(type, out IMessage messageData))
        {
            callFun?.Invoke(null);
            return messageData as T;
        }

        string dataFileName = typeof(T).Name;
        byte[] protoData = await GetBytesAsync(dataFileName, token);
        if (protoData is { Length: 0 })
        {
            callFun?.Invoke(null);
            return null;
        }

        T temp = Activator.CreateInstance(typeof(T)) as T;
        try
        {
            temp.MergeFrom(protoData);
            if (temp is IProtoInit protoInit)
            {
                protoInit.Init();
            }
        }
        catch (Exception e)
        {
            ProtoDebug.LogError($"数据反序列化失败err：{e.Message}");
        }


        protoDataDict.TryAdd(type, temp);

        callFun?.Invoke(temp);
        return temp;
    }

    public virtual async Task<byte[]> GetBytesAsync(string dataFileName, CancellationToken token = default)
    {
        if (!File.Exists(dataFileName))
        {
            return null;
        }

        byte[] protoData = await File.ReadAllBytesAsync($"{ProtoDataPath}/{dataFileName}.protodat", token);
        return protoData;
    }
}